#ifndef GAMEENGINE_CORE_RESOURCE_PTR_RESOURCE_HPP
#define GAMEENGINE_CORE_RESOURCE_PTR_RESOURCE_HPP

#include <typeinfo>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//========================================================



//! Default constructor
/*!
 @param nb_elements The number of elements to be created
 @param allocate If set to true, the elements are allocated immediately. Otherwise, the allocation
                 of the elements is deferred.
 @param context The context in which the elements are available. This parameter is used by the resource
                manager to desambiguate resources.
 */
template <class T, class A>
PtrResource<T,A>::PtrResource(ElementsCount nb_elements, bool allocate, CONTEXTID context) :
	nb_elements_(nb_elements), context_(context), id_(nil)
{
	if ( context_ < 0 ) context_ = RttiManager::instance()->find_or_assign_rtti( typeid( TYPEID ).name() );
	if ( allocate == true ) create_id();
}

//! Copy constructor
/*!
 @param rhs The pointer resource to copy
 */
template <class T, class A>
PtrResource<T,A>::PtrResource(const PtrResource& rhs) : id_(rhs.id_), context_(rhs.context()) {
	if ( id_ != nil ) ResourceManager<ID,CONTEXTID>::instance()->reference_resource(*this);
}

//! Destructor
template <class T, class A>
PtrResource<T,A>::~PtrResource() {
	clean_id();
}

//! Assignment operator
/*!
 @param rhs The pointer resource to copy
 */
template <class T, class A>
PtrResource<T,A>& PtrResource<T,A>::operator=(const PtrResource& rhs) {
	if ( &rhs != this ) {
		ResourceManager<ID,CONTEXTID>* manager = (ResourceManager<ID,CONTEXTID>*)ResourceManager<ID,CONTEXTID>::instance();
		if ( id_ != nil ) manager->unreference_resource(*this);
		context_ = rhs.context();
		id_ = rhs.id_;
		if ( id_ != nil ) manager->reference_resource(*this);
	}
	return *this;
}

//! Indirection operator
/*!
 @return A reference to the internal data
 */
template <class T, class A>
T& PtrResource<T,A>::operator*() {
	return *id_;
}

//! Pointer-to-member operator
/*!
 @return A pointer to the internal data
 */
template <class T, class A>
T* PtrResource<T,A>::operator->() {
	return id_;
}

//! Subscript operator
/*!
 @return A subscript reference to the internal data
 */
template <class T, class A>
T& PtrResource<T,A>::operator[](const int location) {
	return id_[location];
}

//! Clean the previous ID
template <class T, class A>
void PtrResource<T,A>::clean_id() {
	// Nothing to do if ID is not allocated
	if ( id_ == nil ) return;

	// Unreference ID from the resource manager
	ResourceManager<ID,CONTEXTID>::instance()->unreference_resource(*this);

	// Clean ID member variable
	id_ = nil;
}

//! Allocate a new ID
template <class T, class A>
void PtrResource<T,A>::create_id() {
	// Clean previous ID, if any
	clean_id();
	if ( nb_elements_ <= 0 ) return;

	// Generate a new ID
	id_ = A::allocate(nb_elements_);
	MemoryManager::instance()->reference(*this);

	// Reference this ID
	ResourceManager<ID,CONTEXTID>::instance()->reference_resource(*this);
}

//! Desallocate the pointer ID
template <class T, class A>
void PtrResource<T,A>::destroy_resource() const {
	if ( id_ != nil ) {
		A::desallocate(id_, nb_elements_);
		MemoryManager::instance()->dereference(*this);
	}
}

//! Return the pointer context
template <class T, class A>
typename PtrResource<T,A>::CONTEXTID PtrResource<T,A>::context() const {
	return context_;
}

//! Return the pointer reference
template <class T, class A>
typename PtrResource<T,A>::ID PtrResource<T,A>::reference() const {
	return id_;
}

//========================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
